{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Training a Neural Network using Augmentor and Keras\n",
    "\n",
    "In this notebook, we will train a simple convolutional neural network on the MNIST dataset using Augmentor to augment images on the fly using a generator.\n",
    "\n",
    "## Import Required Libraries\n",
    "\n",
    "We start by making a number of imports:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import Augmentor\n",
    "\n",
    "import keras\n",
    "from keras.models import Sequential\n",
    "from keras.layers import Dense, Dropout, Flatten\n",
    "from keras.layers import Conv2D, MaxPooling2D\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define a Convolutional Neural Network\n",
    "\n",
    "Once the libraries have been imported, we define a small convolutional neural network. See the Keras documentation for details of this network: <https://github.com/fchollet/keras/blob/master/examples/mnist_cnn.py> \n",
    "\n",
    "It is a three layer deep neural network, consisting of 2 convolutional layers and a fully connected layer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_classes = 10\n",
    "input_shape = (28, 28, 1)\n",
    "\n",
    "model = Sequential()\n",
    "model.add(Conv2D(32, kernel_size=(3, 3),\n",
    "                 activation='relu',\n",
    "                 input_shape=input_shape))\n",
    "model.add(Conv2D(64, (3, 3), activation='relu'))\n",
    "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "model.add(Dropout(0.25))\n",
    "model.add(Flatten())\n",
    "model.add(Dense(128, activation='relu'))\n",
    "model.add(Dropout(0.5))\n",
    "model.add(Dense(num_classes, activation='softmax'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once a network has been defined, you can compile it so that the model is ready to be trained with data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.compile(loss=keras.losses.categorical_crossentropy,\n",
    "              optimizer=keras.optimizers.Adadelta(),\n",
    "              metrics=['accuracy'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can view a summary of the network using the `summary()` function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d_1 (Conv2D)            (None, 26, 26, 32)        320       \n",
      "_________________________________________________________________\n",
      "conv2d_2 (Conv2D)            (None, 24, 24, 64)        18496     \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 12, 12, 64)        0         \n",
      "_________________________________________________________________\n",
      "dropout_1 (Dropout)          (None, 12, 12, 64)        0         \n",
      "_________________________________________________________________\n",
      "flatten_1 (Flatten)          (None, 9216)              0         \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 128)               1179776   \n",
      "_________________________________________________________________\n",
      "dropout_2 (Dropout)          (None, 128)               0         \n",
      "_________________________________________________________________\n",
      "dense_2 (Dense)              (None, 10)                1290      \n",
      "=================================================================\n",
      "Total params: 1,199,882\n",
      "Trainable params: 1,199,882\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Use Augmentor to Scan Directory for Data\n",
    "\n",
    "Now we will use Augmentor to scan a directory containing our data that we will eventually feed into the neural network in order to train it. \n",
    "\n",
    "When you point a pipeline to a directory, it will scan each subdirectory and treat each subdirectory as a class for your machine learning problem. \n",
    "\n",
    "For example, within the directory `mnist`, there are subdirectories for each digit:\n",
    "\n",
    "```\n",
    "mnist/\n",
    "├── 0/\n",
    "│   ├── 0001.png\n",
    "│   ├── 0002.png\n",
    "│   ├── ...\n",
    "│   └── 5985.png\n",
    "├── 1/\n",
    "│   ├── 0001.png\n",
    "│   ├── 0002.png\n",
    "│   ├── ...\n",
    "│   └── 6101.png\n",
    "├── 2/\n",
    "│   ├── 0000.png\n",
    "│   ├── 0001.png\n",
    "│   ├── ...\n",
    "│   └── 5801.png\n",
    "│ ...\n",
    "├── 9/\n",
    "│   ├── 0001.png\n",
    "│   ├── 0002.png\n",
    "│   ├── ...\n",
    "│   └── 6001.png\n",
    "└\n",
    "```\n",
    "\n",
    "The directory `0` contains all the images corresponding to the 0 class.\n",
    "\n",
    "To get the data, we can use `wget` (this may not work under Windows):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--2018-03-23 15:15:37--  https://rawgit.com/myleott/mnist_png/master/mnist_png.tar.gz\n",
      "Resolving rawgit.com (rawgit.com)... 104.18.62.176, 104.18.63.176, 2400:cb00:2048:1::6812:3eb0, ...\n",
      "Connecting to rawgit.com (rawgit.com)|104.18.62.176|:443... connected.\n",
      "HTTP request sent, awaiting response... 301 Moved Permanently\n",
      "Location: https://raw.githubusercontent.com/myleott/mnist_png/master/mnist_png.tar.gz [following]\n",
      "--2018-03-23 15:15:37--  https://raw.githubusercontent.com/myleott/mnist_png/master/mnist_png.tar.gz\n",
      "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.112.133\n",
      "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.112.133|:443... connected.\n",
      "HTTP request sent, awaiting response... 200 OK\n",
      "Length: 15683414 (15M) [application/octet-stream]\n",
      "Saving to: ‘mnist_png.tar.gz’\n",
      "\n",
      "100%[======================================>] 15,683,414  9.06MB/s   in 1.7s   \n",
      "\n",
      "2018-03-23 15:15:38 (9.06 MB/s) - ‘mnist_png.tar.gz’ saved [15683414/15683414]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!wget https://rawgit.com/myleott/mnist_png/master/mnist_png.tar.gz\n",
    "!tar -xf mnist_png.tar.gz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After the MNIST data has downloaded, we can instantiate a `Pipeline` object in the `training` directory to add the images to the current pipeline:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initialised with 60000 image(s) found.\n",
      "Output directory set to mnist_png/training/output."
     ]
    }
   ],
   "source": [
    "p = Augmentor.Pipeline(\"mnist_png/training\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Add Operations to the Pipeline\n",
    "\n",
    "Now that a pipeline object `p` has been created, we can add operations to the pipeline. Below we add several simple  operations:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.flip_top_bottom(probability=0.1)\n",
    "p.rotate(probability=0.3, max_left_rotation=5, max_right_rotation=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can view the status of pipeline using the `status()` function, which shows information regarding the number of classes in the pipeline, the number of images, and what operations have been added to the pipeline:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Operations: 2\n",
      "\t0: Flip (top_bottom_left_right=TOP_BOTTOM probability=0.1 )\n",
      "\t1: RotateRange (max_right_rotation=5.0 max_left_rotation=-5.0 probability=0.3 )\n",
      "Images: 60000\n",
      "Classes: 10\n",
      "\tClass index: 0 Class label: 0 \n",
      "\tClass index: 1 Class label: 1 \n",
      "\tClass index: 2 Class label: 2 \n",
      "\tClass index: 3 Class label: 3 \n",
      "\tClass index: 4 Class label: 4 \n",
      "\tClass index: 5 Class label: 5 \n",
      "\tClass index: 6 Class label: 6 \n",
      "\tClass index: 7 Class label: 7 \n",
      "\tClass index: 8 Class label: 8 \n",
      "\tClass index: 9 Class label: 9 \n",
      "Dimensions: 1\n",
      "\tWidth: 28 Height: 28\n",
      "Formats: 1\n",
      "\t PNG\n",
      "\n",
      "You can remove operations using the appropriate index and the remove_operation(index) function.\n"
     ]
    }
   ],
   "source": [
    "p.status()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating a Generator\n",
    "\n",
    "A generator will create images indefinitely, and we can use this generator as input into the model created above. The generator is created with a user-defined batch size, which we define here in a variable named `batch_size`. This is used later to define number of steps per epoch, so it is best to keep it stored as a variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "batch_size = 128\n",
    "g = p.keras_generator(batch_size=batch_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The generator can now be used to created augmented data. In Python, generators are invoked using the `next()` function - the Augmentor generators will return images indefinitely, and so `next()` can be called as often as required. \n",
    "\n",
    "You can view the output of generator manually:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "images, labels = next(g)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Images, and their labels, are returned in batches of the size defined above by `batch_size`. The `image_batch` variable is a tuple, containing the augmentented images and their corresponding labels.\n",
    "\n",
    "To see the label of the first image returned by the generator you can use the array's index:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 0 0 0 0 1 0 0 0 0]\n"
     ]
    }
   ],
   "source": [
    "print(labels[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or preview the images using Matplotlib (the image should be a 5, according to the label information above):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nKMIPBPX/WkNh2s7INOEAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7ff6e6ffa550>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.imshow(images[0].reshape(28, 28), cmap=\"Greys\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train the Network\n",
    "\n",
    "We train the network by passing the generator, `g`, to the model's fit function. In Keras, if a generator is used we used the `fit_generator()` function as opposed to the standard `fit()` function. Also, the steps per epoch should roughly equal the total number of images in your dataset divided by the `batch_size`.\n",
    "\n",
    "Training the network over 5 epochs, we get the following output:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/5\n",
      "468/468 [==============================] - 30s 65ms/step - loss: 0.4860 - acc: 0.8478\n",
      "Epoch 2/5\n",
      "468/468 [==============================] - 29s 63ms/step - loss: 0.2026 - acc: 0.9392\n",
      "Epoch 3/5\n",
      "468/468 [==============================] - 29s 61ms/step - loss: 0.1611 - acc: 0.9523\n",
      "Epoch 4/5\n",
      "468/468 [==============================] - 28s 60ms/step - loss: 0.1405 - acc: 0.9582\n",
      "Epoch 5/5\n",
      "468/468 [==============================] - 28s 59ms/step - loss: 0.1203 - acc: 0.9645\n"
     ]
    }
   ],
   "source": [
    "h = model.fit_generator(g, steps_per_epoch=len(p.augmentor_images)/batch_size, epochs=5, verbose=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "Using Augmentor with Keras means only that you need to create a generator when you are finished creating your pipeline. This has the advantage that no images need to be saved to disk and are augmented on the fly."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
